<template>
  <component :is="wrapperComponent" :offset-top="offsetTop" :offset-bottom="offsetBottom" @on-change="handleAffixStateChange">
    <div :class="`${prefixCls}-wrapper`" :style="wrapperStyle">
      <div :class="`${prefixCls}`">
        <div :class="`${prefixCls}-ink`">
          <span v-show="showInk" :class="`${prefixCls}-ink-ball`" :style="{top: `${inkTop}px`}"></span>
        </div>
        <slot></slot>
      </div>
    </div>
  </component>
</template>

<script setup>
defineOptions({
  name: 'Anchor'
})
import {nextTick,ref,provide, getCurrentInstance, computed, watch,onMounted,onBeforeUnmount} from "vue"
import { useRouter,useRoute } from 'vue-router'
import {scrollTop,sharpMatcherRegx} from "../../utils/assist"
import {on,off} from "../../utils/dom"
import {isClient} from "../../utils/index"

const props=defineProps({
  affix: {
    type: Boolean,
    default: true
  },
  offsetTop: {
    type: Number,
    default: 0
  },
  offsetBottom: Number,
  bounds: {
    type: Number,
    default: 5
  },
  //        container: [String, HTMLElement],  // HTMLElement 在 SSR 下不支持
  container: null,
  showInk: {
    type: Boolean,
    default: false
  },
  scrollOffset: {
    type: Number,
    default: 0
  }
})

const emits=defineEmits(['on-change','on-select'])

const prefixCls=ref('haloe-anchor');
let isAffixed=ref(false) // current affixed state
let inkTop= ref(0);
let animating=ref(false) // if is scrolling now
let currentLink=ref('') // current show link =>  #href -> currentLink = #href
let currentId=ref('') // current show title id =>  #href -> currentId = href
let scrollContainer=ref(null)
let scrollElement=ref(null)
// titlesOffsetArr: [],
let  wrapperTop=ref(0)
let upperFirstTitle=ref(true)
let links=ref([])

let $route=useRoute()

const wrapperComponent=computed(()=>{
  return props.affix?'e-affix':'div';
})

const wrapperStyle=computed(()=>{
  return {
    maxHeight:props.offsetTop?`calc(100vh - ${props.offsetTop}px)`:'100vh'
  }
})

const containerIsWindow=computed(()=>{
  return scrollContainer.value===window
})

const titlesOffsetArr=computed(()=>{
  const _links=links.value.map(item=>{
    return item.link.href;
  })

  const idArr=_links.map(link=>{
    return link.split('#')[1]
  })

  let offsetArr=[];
  // console.log('idArr',idArr)
  isClient&& idArr.forEach(id => {
    const titleEle=document.getElementById(id);
    if(titleEle){
      offsetArr.push({
        link:`#${id}`,
        offset:titleEle.offsetTop-scrollElement.value.offsetTop
      })
    }
  });

  return offsetArr
})


const handleAffixStateChange=(state)=>{
  isAffixed.value=props.affix&&state
}

function handleSetInkTop () {
  if (!isClient) return;
  const currentLinkElementA = document.querySelector(`a[data-href="${currentLink.value}"]`);
  // console.log('currentLinkElementA',currentLinkElementA)
  if (!currentLinkElementA) return;
  const elementATop = currentLinkElementA.offsetTop;
  const top = (elementATop < 0 ? props.offsetTop : elementATop);
  inkTop.value = top;
}

function getCurrentScrollAtTitleId (scrollTop) {
  let i = -1;
  let len =titlesOffsetArr.value.length;
  let titleItem = {
    link: '#',
    offset: 0
  };
  scrollTop += props.bounds;
  while (++i < len) {
    let currentEle = titlesOffsetArr.value[i];
    let nextEle = titlesOffsetArr.value[i + 1];
    if (scrollTop >= currentEle.offset && scrollTop < ((nextEle && nextEle.offset) || Infinity)) {
      titleItem = titlesOffsetArr.value[i];
      break;
    }
  }
  currentLink.value = titleItem.link;
  handleSetInkTop();
}

// const handleScroll=(e)=>{
//   upperFirstTitle.value=!!titlesOffsetArr.value[0]&&e.target.scrollTop<titlesOffsetArr.value[0].offset
//   if(animating.value) return;
//   const scrollTop=isClient?(document.documentElement.scrollTop||document.body.scrollTop||e.target.scrollTop):0;
//   console.log('scrollTop',scrollTop)
//   getCurrentScrollAtTitleId(scrollTop);
// }

function handleScroll(e){
  upperFirstTitle.value=!!titlesOffsetArr.value[0]&&e.target.scrollTop<titlesOffsetArr.value[0].offset
  if(animating.value) return;
  // const scrollTop=isClient?(document.documentElement.scrollTop||document.body.scrollTop||e.target.scrollTop):0;
  // console.log('scrollTop',scrollTop,document.documentElement.scrollTop,document.body.scrollTop,e.target.scrollTop)
  // getCurrentScrollAtTitleId(scrollTop);
  const scrollTop=isClient?(e.target.scrollTop||document.documentElement.scrollTop||document.body.scrollTop):0;
  // console.log('e.target',e.target)
  getCurrentScrollAtTitleId(scrollTop)
}

const handleHashChange =()=> {
  if (!isClient) return;
  const url = window.location.href;
  const sharpLinkMatch = sharpMatcherRegx.exec(url);
  if (!sharpLinkMatch) return;
  currentLink.value = sharpLinkMatch[0];
  currentId.value = sharpLinkMatch[1];
  // console.log('currentLink',currentLink.value,currentId.value)
}

const handleScrollTo =()=> {
  if (!isClient) return;
  const anchor = document.getElementById(currentId.value);
  // console.log('currentId',currentId.value)
  // console.log('anchor////',anchor)
  const currentLinkElementA = document.querySelector(`a[data-href="${currentLink.value}"]`);
  // console.log('currentLinkElementA',currentLinkElementA)
  let offset = props.scrollOffset;
  if (currentLinkElementA) {
    offset = parseFloat(currentLinkElementA.getAttribute('data-scroll-offset'));
  }

  if (!anchor) return;
  const offsetTop = anchor.offsetTop - wrapperTop.value - offset;
  animating.value = true;
  // console.log(scrollElement.value.scrollTop,offsetTop)
  // console.log('scrollContainer///',scrollContainer.value,scrollElement.value,scrollElement.value.scrollTop)
  
  scrollTop(scrollContainer.value, scrollElement.value.scrollTop, offsetTop, 600, () => {
    animating.value = false;
  });
  handleSetInkTop();
}

const getContainer= ()=> {
  if (!isClient) return;
  scrollContainer.value = props.container ? (typeof props.container === 'string' ? document.querySelector(props.container) : props.container) : window;
  scrollElement.value = props.container ? scrollContainer.value : (document.documentElement || document.body);
  // console.log('scrollContainer',scrollContainer.value)
  // console.log('scrollElement',scrollElement.value)
}
const removeListener= ()=> {
  off(scrollContainer.value, 'scroll', handleScroll);
  off(window, 'hashchange', handleHashChange);
}

const init= ()=> {
  // const anchorLink = findComponentDownward(this, 'AnchorLink');
  handleHashChange();
  // on(scrollContainer.value, 'scroll', handleScroll);
  nextTick(() => {
    removeListener();
    getContainer();
    wrapperTop.value = containerIsWindow.value ? 0 : scrollElement.value.offsetTop;
    handleScrollTo();
    handleSetInkTop();
    // this.updateTitleOffset();
    // console.log('titlesOffsetArr',titlesOffsetArr.value)
    // console.log('titlesOffsetArr///',titlesOffsetArr.value)
    if (titlesOffsetArr.value[0]) {
      upperFirstTitle.value= scrollElement.value.scrollTop < titlesOffsetArr.value[0].offset;
    }
    on(scrollContainer.value, 'scroll', handleScroll,true);
    // console.log('scrollContainer///',scrollContainer.value)
    on(window, 'hashchange', handleHashChange);
  });
}


watch($route,(val)=>{
  currentLink.value = '';
  currentId.value = '';
  // console.log('watch////',val)
  handleHashChange();
  nextTick(() => {
    // setTimeout(()=>handleScrollTo(),0)
    handleScrollTo();
  });
})

watch(()=>props.container,()=>{
  init()
})

watch(()=>currentLink.value,(newHref,oldHref)=>{
  emits('on-change',newHref,oldHref)
})


const addLink= (id, link)=> {
  links.value.push({ id, link });
}
const removeLink= (id)=> {
  const linkIndex = links.value.findIndex(item => item.id === id);
  links.value.splice(linkIndex, 1);
}

const onSelect=(href)=>{
  emits('on-select',href)
}

onMounted(()=>{
  init()
  // window.addEventListener('scroll',(e)=>{
  //   console.log('scroll,xxx',e)
  // },true)
})

onBeforeUnmount(() => {
  removeListener()
})


// provide('AnchorInstance',getCurrentInstance().proxy)
provide('AnchorInstance',{
  // currentLink:()=>currentLink.value,
  // scrollOffset:()=>props.scrollOffset,
  // currentLink:()=>currentLink.value,
  // currentLink:computed(()=>currentLink.value),
  scrollOffset:computed(()=>props.scrollOffset),
  currentLink,
  // scrollOffset,
  handleHashChange,
  handleScrollTo,
  addLink,
  removeLink,
  init,
  onSelect
})
</script>
